<template>
	<view class="uni-file-picker">
		<view v-if="title" class="uni-file-picker__header">
			<text class="file-title">{{title}}</text>
			<text class="file-count">{{filesList.length}}/{{limitLength}}</text>
		</view>
		<upload-image v-if="fileMediatype === 'image'  && showType === 'grid'" :readonly="readonly" :image-styles="imageStyles"
		 :files-list="filesList" :limit="limitLength" :disablePreview="disablePreview" :delIcon="delIcon" @uploadFiles="uploadFiles"
		 @choose="choose" @delFile="delFile">
			<slot>
				<view class="is-add">
					<view class="icon-add"></view>
					<view class="icon-add rotate"></view>
				</view>
			</slot>
		</upload-image>
		<upload-file v-if="(fileMediatype !== 'image' || showType !== 'grid')" :readonly="readonly" :list-styles="listStyles"
		 :files-list="filesList" :showType="showType" :delIcon="delIcon" @uploadFiles="uploadFiles" @choose="choose" @delFile="delFile">
			<slot><button type="primary" size="mini">选择文件</button></slot>
		</upload-file>
	</view>
</template>

<script>

	/**
	 * FilePicker
	 * @description 文件选择上传组件，可以选择图片、视频等任意文件并上传到当前绑定的服务空间
	 * @tutorial https://ext.dcloud.net.cn/plugin?id=-1
	 * @property {Object|Array}	value	组件数据，通常用来回显 ,类型由return-type属性决定
	 * @property {Boolean}	disabled=[true|false]	组件禁用
	 * 	@value true 	禁用
	 * 	@value false 	取消禁用
	 * @property {Boolean}	readonly=[true|false]	组件只读，不可选择，不显示进度，不显示删除按钮
	 * 	@value true 	只读
	 * 	@value false 	取消只读
	 * @property {String}	return-type=[array|object]	限制 value 格式，当为 object 时 ，组件只能单选，且会覆盖
	 * 	@value array	规定 value 属性的类型为数组
	 * 	@value object	规定 value 属性的类型为对象
	 * @property {Boolean}	disable-preview=[true|false]	禁用图片预览，仅 mode:grid 时生效
	 * 	@value true 	禁用图片预览
	 * 	@value false 	取消禁用图片预览
	 * @property {Boolean}	del-icon=[true|false]	是否显示删除按钮
	 * 	@value true 	显示删除按钮
	 * 	@value false 	不显示删除按钮
	 * @property {Boolean}	auto-upload=[true|false]	是否自动上传，值为true则只触发@select,可自行上传
	 * 	@value true 	自动上传
	 * 	@value false 	取消自动上传
	 * @property {Number|String}	limit	最大选择个数 ，h5 会自动忽略多选的部分
	 * @property {String}	title	组件标题，右侧显示上传计数
	 * @property {String}	mode=[list|grid]	选择文件后的文件列表样式
	 * 	@value list 	列表显示
	 * 	@value grid 	宫格显示
	 * @property {String}	file-mediatype=[image|video|all]	选择文件类型
	 * 	@value image	只选择图片
	 * 	@value video	只选择视频
	 * 	@value all		选择所有文件
	 * @property {Array}	file-extname	选择文件后缀，根据 file-mediatype 属性而不同
	 * @property {Object}	list-style	mode:list 时的样式
	 * @property {Object}	image-styles	选择文件后缀，根据 file-mediatype 属性而不同
	 * @event {Function} select 	选择文件后触发
	 * @event {Function} progress 文件上传时触发
	 * @event {Function} success 	上传成功触发
	 * @event {Function} fail 		上传失败触发
	 * @event {Function} delete 	文件从列表移除时触发
	 */

	import {
		chooseAndUploadFile
	} from './choose-and-upload-file.js'
	import uploadImage from './upload-image.vue'
	import uploadFile from './upload-file.vue'
	let fileInput = null
	export default {
		name: "uniFilePicker",
		components: {
			uploadImage,
			uploadFile
		},
		props: {
			value: {
				type: [Array, Object],
				default () {
					return []
				}
			},
			disabled: {
				type: Boolean,
				default: false
			},
			disablePreview: {
				type: Boolean,
				default: false
			},
			delIcon: {
				type: Boolean,
				default: true
			},
			// 自动上传
			autoUpload: {
				type: Boolean,
				default: true
			},
			// 最大选择个数 ，h5只能限制单选或是多选
			limit: {
				type: [Number, String],
				default: 9
			},
			// 列表样式 grid | list | list-card
			mode: {
				type: String,
				default: 'grid'
			},
			// inputUrl: {
			// 	type: Boolean,
			// 	default: false
			// },
			// 选择文件类型  image/video/all
			fileMediatype: {
				type: String,
				default: 'image'
			},
			// 文件类型筛选
			fileExtname: {
				type: Array,
				default () {
					return []
				}
			},
			title: {
				type: String,
				default: ''
			},
			listStyles: {
				type: Object,
				default () {
					return {
						// 是否显示边框
						border: true,
						// 是否显示分隔线
						dividline: true,
						// 线条样式
						borderStyle: {}
					}
				}
			},
			imageStyles: {
				type: Object,
				default () {
					return {
						width: 'auto',
						height: 'auto'
					}
				}
			},
			readonly: {
				type: Boolean,
				default: false
			},
			returnType: {
				type: String,
				default: 'array'
			}
		},
		watch: {
			value: {
				handler(newVal) {
					let newFils = []
					let newData = [].concat(newVal || [])
					newData.forEach(v => {
						const files = this.files.find(i => i.url === v.url)
						const reg = /cloud:\/\/([\w.]+\/?)\S*/
						if (!v.path) {
							v.path = v.url
						}
						if (reg.test(v.url)) {
							this.getTempFileURL(v, v.url)
						}
						newFils.push(files ? files : v)
					})
					this.files = newFils
				},
				immediate: true
			}
		},
		data() {
			return {
				files: []
			}
		},
		computed: {
			filesList() {
				let files = []
				this.files.forEach(v => {
					files.push(v)
				})
				return files
			},
			showType() {
				if (this.fileMediatype === 'image') {
					return this.mode
				}
				return 'list'
			},
			limitLength() {
				if (this.returnType === 'object') {
					return 1
				}
				if (!this.limit) {
					return 1
				}
				if (this.limit >= 9) {
					return 9
				}
				return this.limit
			}
		},
		created() {
			// this.files = Object.assign([], this.value)
			this.tempData = {}
		},
		methods: {
			/**
			 * 继续上传
			 */
			upload() {
				// TODO 先放弃这个实现 ，不能全部上传
				// if (this.$uploadFiles) {
				// 	this.$uploadFiles()
				// } else {
				// 	uni.showToast({
				// 		title: '请先选择文件',
				// 		icon: 'none'
				// 	})
				// }
				let files = []
				this.files.forEach((v, index) => {
					if (v.status === 'ready' || v.status === 'error') {
						files.push(Object.assign({}, v))
					}
				})

				this.uploadFiles(files)
			},
			/**
			 * 选择文件
			 */
			choose() {
				if (this.disabled) return
				if (this.files.length >= Number(this.limitLength) && this.showType !== 'grid' && this.returnType === 'array') {
					uni.showToast({
						title: `您最多选择 ${this.limitLength} 个文件`,
						icon: 'none'
					})
					return
				}
				// uni.showActionSheet({
				// 	itemList: ['填写 url 地址', '选择文件'],
				// 	success: (res) => {
				// 		if (res.tapIndex === 1) {
				// 			this.chooseFiles()
				// 		}
				// 	},
				// 	fail: function(res) {}
				// });
				this.chooseFiles()

			},

			/**
			 * 选择文件并上传
			 */
			chooseFiles() {

				// API 正式发布前，使用本地API上传函数
				if(!uniCloud.chooseAndUploadFile){
					uniCloud.chooseAndUploadFile = chooseAndUploadFile
				}

				uniCloud.chooseAndUploadFile({
					type: this.fileMediatype,
					compressed: false,
					// TODO 如果为空，video 有问题
					extension: this.fileExtname.length> 0 ? this.fileExtname: undefined,
					count: this.limitLength - this.files.length, //默认9
					onChooseFile: async (res) => {
						if ((Number(this.limitLength) === 1 && this.disablePreview && !this.disabled) || this.returnType === 'object') {
							this.files = []
						}
						let filePaths = []
						let files = []
						if (this.fileExtname && this.fileExtname.length > 0) {
							res.tempFiles.forEach(v => {
								let fileFullName = this.getFileExt(v.name)
								const extname = fileFullName.ext.toLowerCase()
								if (this.fileExtname.indexOf(extname) !== -1) {
									files.push(v)
									filePaths.push(v.path)
								}
							})
							if (files.length !== res.tempFiles.length) {
								uni.showToast({
									title: `当前选择了${res.tempFiles.length}个文件 ，${res.tempFiles.length - files.length} 个文件格式不正确`,
									icon: 'none',
									duration: 5000
								})
							}
						} else {
							filePaths = res.tempFilePaths
							files = res.tempFiles
						}


						let currentData = []
						for (let i = 0; i < files.length; i++) {
							if (this.limitLength - this.files.length <= 0) break
							files[i].uuid = Date.now()
							let filedata = await this.getFileData(files[i], this.fileMediatype)
							filedata.progress = 0
							filedata.status = 'ready'
							this.files.push(filedata)
							currentData.push(filedata)
						}
						this.$emit('select', {
							tempFiles: currentData,
							tempFilePaths: filePaths,
						})

						// 停止自动上传
						if (!this.autoUpload) {
							res.tempFiles = []
							// TODO 先放弃这个实现 ，不能全部上传
							// return new Promise((resolve) => {
							// 	this.$uploadFiles = () => {
							// 		// this._is_uploading = true
							// 		resolve(res)
							// 	}
							// })
						}
					},
					onUploadProgress: (progressEvent) => {
						this.setProgress(progressEvent, progressEvent.index)
					},
				}).then(result => {
					this.setSuccessAndError(result.tempFiles)

				}).catch(err => {
					console.log('选择失败', err);
				})
			},

			/**
			 * 批传
			 * @param {Object} e
			 */
			uploadFiles(files) {
				files = [].concat(files)
				this.uploadCloudFiles(files, 5, (res) => {
					this.setProgress(res, res.index, true)
				}).then(result => {
					this.setSuccessAndError(result)
				}).catch(err => {
					console.log('err', err);
				})
			},

			/**
			 * 成功或失败
			 */
			async setSuccessAndError(res, fn) {
				let successData = []
				let errorData = []
				let tempFilePath = []
				let errorTempFilePath = []
				for (let i = 0; i < res.length; i++) {
					// const index  = item.index
					const item = res[i]
					const index = item.uuid ? this.files.findIndex(p => p.uuid === item.uuid) : item.index
					if (index === -1 || !this.files) break
					if (item.errMsg === 'request:fail') {
						this.files[index].url = item.path
						this.files[index].status = 'error'
						this.files[index].errMsg = item.errMsg
						this.files[index].progress = -1
						errorData.push(this.files[index])
						errorTempFilePath.push(this.files[index].url)
					} else {
						this.files[index].errMsg = ''
						this.files[index].url = item.url
						this.files[index].status = 'success'
						successData.push(this.files[index])
						tempFilePath.push(this.files[index].url)
					}
				}

				if (successData.length > 0) {
					this.setEmit()
					// 状态改变返回
					this.$emit('success', {
						tempFiles: this.backObject(successData),
						tempFilePaths: tempFilePath
					})
				}

				if (errorData.length > 0) {
					this.$emit('fail', {
						tempFiles: this.backObject(errorData),
						tempFilePaths: errorTempFilePath
					})
				}
			},

			/**
			 * 获取进度
			 * @param {Object} progressEvent
			 * @param {Object} index
			 * @param {Object} type
			 */
			setProgress(progressEvent, index, type) {
				const fileLenth = this.files.length
				const percentNum = index / fileLenth * 100
				const percentCompleted = Math.round(
					(progressEvent.loaded * 100) / progressEvent.total
				);
				let idx = index
				if (!type) {
					idx = this.files.findIndex(p => p.uuid === progressEvent.tempFile.uuid)
				}
				if (idx === -1 || !this.files[idx]) return
				this.files[idx].progress = percentCompleted
				// 上传中
				this.$emit('progress', {
					index: idx,
					progress: parseInt(percentCompleted),
					tempFile: this.files[idx],
				})
			},

			/**
			 * 删除
			 * @param {Object} index
			 */
			delFile(index) {
				this.$emit('delete', {
					tempFile: this.files[index],
					tempFilePath: this.files[index].url
				})
				this.files.splice(index, 1)
			},

			/**
			 * 获取文件名和后缀
			 * @param {Object} name
			 */
			getFileExt(name) {
				const last_len = name.lastIndexOf(".");
				const len = name.length;
				return {
					name: name.substring(0, last_len),
					ext: name.substring(last_len + 1, len)
				};
			},

			/**
			 * 获取图片信息
			 * @param {Object} filepath
			 */
			getFileInfo(filepath) {
				return new Promise((resolve, reject) => {
					uni.getImageInfo({
						src: filepath,
						success(res) {
							resolve(res)
						},
						fail(err) {
							reject(err)
						}
					})
				})
			},

			/**
			 * 获取封装数据
			 */
			async getFileData(files, type = 'image') {
				// 最终需要上传数据库的数据
				let fileFullName = this.getFileExt(files.name)
				const extname = fileFullName.ext.toLowerCase()
				let filedata = {
					"name": files.name,
					"uuid": files.uuid,
					"extname": extname || '',
					"cloudPath": files.cloudPath,
					"fileType": files.fileType,
					"url": files.path || files.path,
					"size": files.size, //单位是字节
					"image": {},
					"path": files.path,
					"video": {}
				}
				if (type === 'image') {
					const imageinfo = await this.getFileInfo(files.path)
					filedata.image.width = imageinfo.width
					filedata.image.height = imageinfo.height
					filedata.image.location = imageinfo.path
				}
				return filedata
			},

			/**
			 * 批量上传
			 */
			uploadCloudFiles(files, max = 5, onUploadProgress) {
				files = JSON.parse(JSON.stringify(files))
				const len = files.length;
				let count = 0;
				let self = this
				return new Promise((resolve) => {
					while (count < max) {
						next();
					}

					function next() {
						let cur = count++;
						if (cur >= len) {
							!files.find((item) => !item.url && !item.errMsg) && resolve(files);
							return;
						}
						const fileItem = files[cur];
						const index = self.files.findIndex(v => v.uuid === fileItem.uuid)
						fileItem.url = ''
						delete fileItem.errMsg

						uniCloud
							.uploadFile({
								filePath: fileItem.path,
								cloudPath: fileItem.cloudPath,
								fileType: fileItem.fileType,
								onUploadProgress: (res) => {
									res.index = index
									onUploadProgress &&
										onUploadProgress(res);
								},
							})
							.then((res) => {
								fileItem.url = res.fileID;
								fileItem.index = index
								if (cur < len) {
									next();
								}
							})
							.catch((res) => {
								fileItem.errMsg = res.errMsg || res.message;
								fileItem.index = index
								if (cur < len) {
									next();
								}
							});
					}
				});
			},
			setEmit() {
				let data = []
				if(this.returnType === 'object'){
					data = this.backObject(this.files)[0]
				}else{
					data = this.backObject(this.files)
				}
				this.$emit('input', data)
			},
			backObject(files) {
				let newFilesData = JSON.parse(JSON.stringify(files))
				newFilesData.map(v => {
					delete v.path
					delete v.uuid
					delete v.video
					delete v.progress
					delete v.errMsg
					delete v.status
					delete v.cloudPath
					return v
				})
				return newFilesData
			},
			async getTempFileURL(file, fileList) {
				fileList = {
					fileList: [].concat(fileList)
				}
				const urls = await uniCloud.getTempFileURL(fileList)
				file.path = urls.fileList[0].tempFileURL || ''
				const index = this.files.findIndex(v => v.path === file.path)
				if (index !== -1) {
					this.$set(this.files, index, file)
				}
			}
		}
	}
</script>

<style>
	.uni-file-picker {
		/* #ifndef APP-NVUE */
		box-sizing: border-box;
		overflow: hidden;
		/* #endif */
	}

	.uni-file-picker__header {
		padding-top: 5px;
		padding-bottom: 10px;
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		justify-content: space-between;
	}

	.file-title {
		font-size: 14px;
		color: #333;
	}

	.file-count {
		font-size: 14px;
		color: #999;
	}

	.is-add {
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		align-items: center;
		justify-content: center;
	}

	.icon-add {
		width: 50px;
		height: 5px;
		background-color: #f1f1f1;
		border-radius: 2px;
	}

	.rotate {
		position: absolute;
		transform: rotate(90deg);
	}
</style>
